home *** CD-ROM | disk | FTP | other *** search
/ The Original Shareware 1.1 / The Original Shareware (WeMake CDs)(Volume 1.1)(CDs, Inc)(1993).iso / 19 / madtrb40.zip / MSTREES.PAS < prev    next >
Pascal/Delphi Source File  |  1986-05-04  |  43KB  |  1,040 lines

  1. { *************************************************************************** }
  2. { *                                                                         * }
  3. { *                         MEDIAN SPLIT TREE PROGRAM                       * }
  4. { *                                                                         * }
  5. { *                               Class: CS 577                             * }
  6. { *                          Instructor: Manber                             * }
  7. { *                                                                         * }
  8. { *                          Written by Chris Maeder                        * }
  9. { *                  Edited and Compiled using Turbo Pascal                 * }
  10. { *                              on an IBM PC                               * }
  11. { *                                                                         * }
  12. { *************************************************************************** }
  13.  
  14.  
  15.  
  16. Const
  17.   MAX_KEYS_PER_NODE=3;                 { maximum number of look-up keys for any given tree node,
  18.                                          not including split key }
  19.   MAX_KEY_SIZE=15;                     { maximum size of string used to store a key }
  20.  
  21.  
  22.  
  23. Type
  24.   KeyString=String[MAX_KEY_SIZE];      { a string type used to store look-up keys }
  25.   FrequencyType=0..MAXINT;             { allowable integer values that a key's frquency value can take on }
  26.  
  27.   NodeKeyElement=                      { a look-up key node element type }
  28.     Record
  29.       Key:KeyString;                   { look-up key string }
  30.       Frequency:FrequencyType;         { frequency value }
  31.     End; { NodeKeyElement record }
  32.  
  33.   KeyArray=                            { an array of look-up keys }
  34.     Array[1..MAX_KEYS_PER_NODE] of NodeKeyElement;
  35.  
  36.   TreeNodePtr=^TreeNodeType;           { a pointer which points to the tree node type 'TreeNodeType' }
  37.   TreeNodeType=                        { a n-MST node of }
  38.     Record
  39.       NodeKeys:KeyArray;               { an array of look-up keys }
  40.       SplitKey:NodeKeyElement;         { a key that splits the left and right subtree keys }
  41.       Left:TreeNodePtr;                { a pointer to tree node's left son }
  42.       Right:TreeNodePtr;               { a pointer to tree node's right son }
  43.     End; { TreeNodeType record }
  44.  
  45.   L_L_ElementPtr=^L_L_ElementType;     { a pointer to the doubly linked list element type 'L_L_ElementType' }
  46.   L_L_ElementType=                     { a doubly linked list element }
  47.     Record
  48.       Key:KeyString;                   { look-up key }
  49.       Frequency:FrequencyType;         { frequency value }
  50.       Front:L_L_ElementPtr;            { a pointer to the forward element }
  51.       Back:L_L_ElementPtr;             { a pointer to the rearward element }
  52.     End; { L_L_ElementType record }
  53.  
  54.  
  55.  
  56. Var
  57.   L_L_HeadPtr:L_L_ElementPtr;          { a pointer to the head of the doubly linked list }
  58.   L_L_TailPtr:L_L_ElementPtr;          { a pointer to the tail of the doubly linked list }
  59.   MST_RootPtr:TreeNodePtr;             { a pointer to the root node of the MST }
  60.   HoldPtr:Integer;                     { a pointer used to temporarily store console screen output vector }
  61.  
  62.  
  63.  
  64. Procedure InitProgram;
  65.  
  66. { This procedure initializes all the global variables and pointers used in this
  67.   program. }
  68.  
  69. Begin   { InitProgram }
  70.   L_L_HeadPtr:=Nil;
  71.   L_L_TailPtr:=Nil;
  72.   HoldPtr:=ConOutPtr;
  73. End;    { InitProgram }
  74.  
  75.  
  76.  
  77. Procedure HardCopy(    On:Boolean);
  78.  
  79. { This procedure reroutes the output from the screen to the printer. }
  80.  
  81. Begin   { HardCopy }
  82.   If On Then
  83.     Begin
  84.       HoldPtr:=ConOutPtr;
  85.       ConOutPtr:=LstOutPtr;
  86.     End { If On }
  87.   Else
  88.     Begin
  89.       ConOutPtr:=HoldPtr;
  90.     End; { Else }
  91. End;    { HardCopy }
  92.  
  93.  
  94.  
  95. Procedure ReadInputFileModule;
  96.  
  97. { *************************************************************************** }
  98. { *                                                                         * }
  99. { *                         READ INPUT FILE MODULE                          * }
  100. { *                                                                         * }
  101. { *     This module reads the file entries out of the declared input        * }
  102. { *     file.  Each file entry contains a look-up key and a value           * }
  103. { *     corresponding to its frequency count.                               * }
  104. { *                                                                         * }
  105. { *     The file entries are assumed to already be in sorted order          * }
  106. { *     according to the look-up keys lexical value.  These entries are     * }
  107. { *     then placed in a doubly linked list, maintaining their sorted       * }
  108. { *     lexical order.                                                      * }
  109. { *                                                                         * }
  110. { *************************************************************************** }
  111.  
  112. Const
  113.   MAX_FILE_ENTRY_SIZE=50;              { maximum size of the file entry string }
  114.   FILE_NAME='KeyData1.Dat';            { name of the input data file }
  115.  
  116. Type
  117.   FileEntryString=String[MAX_FILE_ENTRY_SIZE]; { a string type used to store file entries }
  118.  
  119. Var
  120.   DataFile:Text;                       { text file }
  121.   FileEntry:FileEntryString;           { variable used in reading entries out of input data file }
  122.  
  123.  
  124.  
  125.   Procedure RemoveFirstEntryFromString(Var Text:FileEntryString);
  126.  
  127.   { This procedure removes the first text entry from the passed text string and
  128.     returns the new truncated text string.  The text entries in the passed text
  129.     string are assumed to be delinated by one space.  See the following
  130.     example.
  131.  
  132.                 Passed:     Entry1 Entry2 Entry3
  133.                 Returned:   Entry2 Entry3                                  }
  134.  
  135.   Begin   { RemoveFirstEntryFromString }
  136.     If Pos(' ',Text)<>0 Then
  137.       Text:=Copy(Text,(Pos(' ',Text)+1),(Length(Text)-Pos(' ',Text)));
  138.   End;    { RemoveFirstEntryFromString }
  139.  
  140.  
  141.  
  142.   Procedure ReturnFirstEntryFromString(Var Text:FileEntryString);
  143.  
  144.   { This procedure removes the first text entry from the passed text string and
  145.     returns the first text entry.  The text entries in the passed text string
  146.     are assumed to be delinated by one space.  See the following example.
  147.  
  148.                 Passed:     Entry1 Entry2 Entry3
  149.                 Returned:   Entry1                                         }
  150.  
  151.   Begin   { ReturnFirstEntryFromString }
  152.     If Pos(' ',Text)<>0 Then
  153.       Text:=Copy(Text,1,(Pos(' ',Text)-1));
  154.   End;    { ReturnFirstEntryFromString }
  155.  
  156.  
  157.  
  158.   Function DetermineKeyFromFileEntry(    Entry:FileEntryString):KeyString;
  159.  
  160.   { This function is passed a file entry which contains a look-up key as the
  161.     first entry.  This function picks off the look-up key from the file entry
  162.     and passes the look-up key back. }
  163.  
  164.   Begin   { DetermineKeyFromFileEntry }
  165.     ReturnFirstEntryFromString(Entry);
  166.     DetermineKeyFromFileEntry:=Entry;
  167.   End;    { DetermineKeyFromFileEntry }
  168.  
  169.  
  170.  
  171.   Function DetermineFrequencyFromFileEntry(    Entry:FileEntryString):FrequencyType;
  172.  
  173.   { This function is passed a file entry which contains a frequency count for a
  174.     look-up key as the second entry.  This function picks off the frequency
  175.     count from the file entry and passes the frequency count back. }
  176.  
  177.   Var
  178.     Frequency:Integer;                 { a temporary variable used in conversion of string to real }
  179.     ErrorCode:Integer;                 { a variable used in checking for error in conversion }
  180.  
  181.   Begin   { DetermineFrquencyFromFileEntry }
  182.     RemoveFirstEntryFromString(Entry);
  183.     ReturnFirstEntryFromString(Entry);
  184.     Val(Entry,Frequency,ErrorCode);
  185.     DetermineFrequencyFromFileEntry:=Frequency;
  186.   End;    { DetermineFrquencyFromFileEntry }
  187.  
  188.  
  189.  
  190.   Procedure AddKeyToLinkedList(Var HeadPtr,
  191.                                    TailPtr:  L_L_ElementPtr;
  192.                                    FileEntry:FileEntryString);
  193.  
  194.   { This procedure places the passed file entry onto the end of the doubly
  195.     linked list.  By adding the passed file entry to the end of the doubly
  196.     linked list maintains the assumed lexical ordering of the keys. }
  197.  
  198.   Var
  199.     New_L_L_Element:L_L_ElementPtr;    { a pointer to a new doubly linked list element which will be added to the tail
  200.                                          of the passed linked list }
  201.  
  202.   Begin   { AddKeyToLinkedList }
  203.     New(New_L_L_Element);              { create a new queue element to add to the end of the passed linked list }
  204.     With New_L_L_Element^ Do
  205.       Begin                            { routine to enter information into new linked list element }
  206.         Key:=DetermineKeyFromFileEntry(FileEntry);
  207.         Frequency:=DetermineFrequencyFromFileEntry(FileEntry);
  208.         Front:=Nil;                    { set forward pointer to Nil }
  209.         Back:=Nil;                     { set backward pointer to Nil }
  210.       End; { With New_L_L_Element }
  211.     If HeadPtr=Nil Then
  212.       Begin                            { Special Case One - linked list not yet in existance }
  213.         HeadPtr:=New_L_L_Element;      { set passed linked list head pointer to point to first element in the linked list }
  214.         TailPtr:=New_L_L_Element;      { set passed linked list tail pointer to point to first element in the linked list }
  215.       End { If HeadPtr }
  216.     Else
  217.       Begin                            { Special Case Two - linked list in existance }
  218.         TailPtr^.Back:=New_L_L_Element;  { link new element to end of linked list }
  219.         New_L_L_Element^.Front:=TailPtr; { link new element to end of linked list }
  220.         TailPtr:=New_L_L_Element;        { increment tail pointer }
  221.       End; { Else }
  222.   End;    { AddKeyToLinkedList }
  223.  
  224.  
  225.  
  226. Begin   { ReadInputFileModule }
  227.   Assign(DataFile,FILE_NAME);          { assign to a disk file }
  228.   Reset(DataFile);                     { open the file for reading }
  229.   Repeat                               { read in the file entries }
  230.     ReadLn(DataFile,FileEntry);
  231.     AddKeyToLinkedList(L_L_HeadPtr,L_L_TailPtr,FileEntry);
  232.   Until Eof(DataFile);                 { read until end of file found }
  233.   Close(DataFile);                     { close the disk file }
  234. End;    { ReadInputFileModule }
  235.  
  236.  
  237.  
  238. Procedure PrintLinkedList(    L_L_HeadPtr,
  239.                               L_L_TailPtr:L_L_ElementPtr);
  240.  
  241. { This procedure prints out the the doubly linked list containing the look-up
  242.   keys. }
  243.  
  244. Var
  245.   L_L_NodePtr:L_L_ElementPtr;          { a pointer used to traverse the doubly linked list }
  246.  
  247. Begin   { PrintLinkedList }
  248.   WriteLn;
  249.   WriteLn;
  250.   WriteLn('LINKED  LIST  OF LOOK-UP KEYS AND');
  251.   WriteLn('CORRESPONDING FREQUENCIES FOLLOWS:');
  252.   WriteLn('----------------------------------');
  253.   WriteLn;
  254.   WriteLn;
  255.   If L_L_HeadPtr<>Nil Then
  256.     Begin
  257.       L_L_NodePtr:=L_L_HeadPtr;
  258.       WriteLn(L_L_NodePtr^.Key,' ',L_L_NodePtr^.Frequency);
  259.       While L_L_NodePtr<>L_L_TailPtr Do
  260.         Begin
  261.           L_L_NodePtr:=L_L_NodePtr^.Back;
  262.           WriteLn(L_L_NodePtr^.Key,' ',L_L_NodePtr^.Frequency);
  263.         End; { While L_L_NodePtr }
  264.     End { If L_L_HeadPtr }
  265.   Else
  266.     WriteLn('No elements in linked list');
  267. End;    { PrintLinkedList }
  268.  
  269.  
  270.  
  271. Procedure Build_MST_Module;
  272.  
  273. { *************************************************************************** }
  274. { *                                                                         * }
  275. { *                     BUILD MEDIAN SPLIT TREE MODULE                      * }
  276. { *                                                                         * }
  277. { *     This module controls the building of the n-MST (median split        * }
  278. { *     tree).  There are n entries per node where the n-1 most frequently  * }
  279. { *     accessed keys in the subtree and the median of the rest of the      * }
  280. { *     keys in the subtree.                                                * }
  281. { *                                                                         * }
  282. { *     Observe that n=1 produces a balanced binary search tree, n=2        * }
  283. { *     produces a conventional median split tree, and n=total number of    * }
  284. { *     look-up keys produces a sequential search.                          * }
  285. { *                                                                         * }
  286. { *************************************************************************** }
  287.  
  288. Type
  289.   ChildType=(LEFT,RIGHT,ROOT);         { an enumerated data type used to determine how to link child
  290.                                          node with parent node }
  291.  
  292.  
  293.  
  294.   Procedure SortKeyArray(Var SetOfKeys:KeyArray);
  295.  
  296.   { This procedure controls the quicksort on the passed array of look-up keys
  297.     so that they are sorted in ascending order according to their lexical ( or
  298.     alphabetical) order. }
  299.  
  300.  
  301.  
  302.     Procedure QuickSort(    LowElement,
  303.                             HighElement:Integer;
  304.                         Var SetOfKeys:  KeyArray);
  305.  
  306.     { This is a recursive procedure that sorts the passed array of look-up keys
  307.       by first determining a pivotal element and then partitions the remaining
  308.       elements (between LowElement and HighElement of the passed SetOfKeys)
  309.       according to that pivotal element. }
  310.  
  311.     Var
  312.       LowIndex:Integer;                  { an incremented index starting at the low element in the passed set of keys }
  313.       HighIndex:Integer;                 { an incremented index starting at the high element in the passed set of keys }
  314.       PivotalKey:KeyString;              { a look-up key which acts as a pivotal element }
  315.       TemporaryNodeElement:NodeKeyElement; { a node key element used in transposing elements in the set of keys }
  316.  
  317.     Begin   { QuickSort }
  318.       LowIndex:=LowElement;
  319.       HighIndex:=HighElement;
  320.       PivotalKey:=SetOfKeys[(LowElement+HighElement) Div 2].Key;
  321.       Repeat
  322.         While SetOfKeys[LowIndex].Key<PivotalKey Do
  323.           LowIndex:=LowIndex+1;
  324.         While PivotalKey<SetOfKeys[HighIndex].Key Do
  325.           HighIndex:=HighIndex-1;
  326.         If LowIndex<=HighIndex Then
  327.           Begin { transpose elements in key array }
  328.             TemporaryNodeElement:=SetOfKeys[LowIndex];
  329.             SetOfKeys[LowIndex]:=SetOfKeys[HighIndex];
  330.             SetOfKeys[HighIndex]:=TemporaryNodeElement;
  331.             LowIndex:=LowIndex+1;
  332.             HighIndex:=HighIndex-1;
  333.           End; { If LowIndex }
  334.       Until LowIndex>HighIndex;
  335.       If LowElement<HighIndex Then
  336.         QuickSort(LowElement,
  337.                   HighIndex,
  338.                   SetOfKeys);
  339.       If LowElement<HighElement Then
  340.         QuickSort(LowIndex,
  341.                   HighElement,
  342.                   SetOfKeys);
  343.     End;    { QuickSort }
  344.  
  345.  
  346.  
  347.   Begin   { SortKeyArray }
  348.     QuickSort(1,
  349.               MAX_KEYS_PER_NODE,
  350.               SetOfKeys);
  351.   End;    { SortKeyArray }
  352.  
  353.  
  354.  
  355.   Function EmptyLinkedList(    L_L_HeadPtr:L_L_ElementPtr):Boolean;
  356.  
  357.   { This function is used to check if the passed linked list is empty.  If the
  358.     linked list is empty, this function returns as true, else it returns as
  359.     false. }
  360.  
  361.   Begin   { EmptyLinkedList }
  362.     If L_L_HeadPtr=Nil Then
  363.       EmptyLinkedList:=True
  364.     Else
  365.       EmptyLinkedList:=False;
  366.   End;    { EmptyLinkedList }
  367.  
  368.  
  369.  
  370.   Function SplitValue(    NumberOf_L_L_Elements:Integer):Integer;
  371.  
  372.   { This function determines the index to the key that should be used as the
  373.     split key value for the passed number of elements remaining in the doubly
  374.     linked list.
  375.  
  376.     Observe that instead of selecting the median as the split value, a more
  377.     balanced MST can be formed by selecting the key whose index in the lexical
  378.     order is maximized where the split value is given by
  379.  
  380.             SplitValue=((MAX_KEYS_PER_NODE+1)*IntegerMultiplier)+1
  381.  
  382.     closest to median value for the passed number of linked list elements.
  383.  
  384.     This algorithm produces an unbalanced, but complete tree in which all but
  385.     at most one node are completely filled with look-up keys including split
  386.     keys, in which the overflow tends toward the left most son. }
  387.  
  388.   Var
  389.     IntegerMultiplier:Integer;         { an incremented integer used as an exponent }
  390.     MedianValue:Integer;               { an integer variable used to temporaily store the median value }
  391.     PossibleSplitValue:Integer;        { an integer variable used in determining the split value }
  392.  
  393.   Begin   { SplitValue }
  394.     IntegerMultiplier:=1;              { initialized incremented integer }
  395.     MedianValue:=(NumberOf_L_L_Elements Div 2);
  396.     PossibleSplitValue:=1;             { initialize split value }
  397.     While (Abs((IntegerMultiplier*(MAX_KEYS_PER_NODE+1))+1)-MedianValue<=
  398.           Abs(PossibleSplitValue-MedianValue)) Do
  399.       Begin
  400.         PossibleSplitValue:=(IntegerMultiplier*(MAX_KEYS_PER_NODE+1))+1;
  401.         IntegerMultiplier:=IntegerMultiplier+1;
  402.       End; { While Abs }
  403.     SplitValue:=PossibleSplitValue;
  404.   End;    { SplitValue }
  405.  
  406.  
  407.  
  408.   Procedure DetermineSplitKey(Var L_L_HeadPtr,
  409.                                   L_L_TailPtr,
  410.                                   L_L_SplitKey,
  411.                                   Left_L_L_HeadPtr,
  412.                                   Left_L_L_TailPtr,
  413.                                   Right_L_L_HeadPtr,
  414.                                   Right_L_L_TailPtr:L_L_ElementPtr);
  415.  
  416.   { This procedure from the linked list returns the median split key element
  417.     and the newly split linked list (at the split key element) to the calling
  418.     routine. }
  419.  
  420.   Var
  421.     Count:Integer;                     { a variable used to determine number of elements in the passed linked list }
  422.     L_L_Element:L_L_ElementPtr;        { a pointer used in traversing the passed linked list }
  423.     L_L_Index:Integer;                 { a variable used to traverse the linked list }
  424.  
  425.   Begin   { DetermineSplitKey }
  426.     If Not EmptyLinkedList(L_L_HeadPtr) Then
  427.       Begin
  428.         { determine number of elements in passed linked list }
  429.         Count:=1;                      { initialize counter }
  430.         L_L_Element:=L_L_HeadPtr;      { initialize traversal element }
  431.         While L_L_Element<>L_L_TailPtr Do
  432.           Begin
  433.             Count:=Count+1;            { increment counter }
  434.             L_L_Element:=L_L_Element^.Back; { increment pointer }
  435.           End; { While L_L_Element }
  436.         { locate split key element }
  437.         L_L_Element:=L_L_HeadPtr;      { initialize traversal element }
  438.         For L_L_Index:=2 To SplitValue(Count) Do
  439.           L_L_Element:=L_L_Element^.Back;
  440.         { split key has been found }
  441.         L_L_SplitKey:=L_L_Element;
  442.         { now split current linked list about split key }
  443.         { split left half of linked list }
  444.         If L_L_SplitKey<>L_L_HeadPtr Then
  445.           Begin
  446.             Left_L_L_HeadPtr:=L_L_HeadPtr;
  447.             Left_L_L_TailPtr:=L_L_SplitKey^.Front;
  448.             Left_L_L_TailPtr^.Back:=Nil;
  449.           End { If L_L_SplitKey }
  450.         Else
  451.           Begin
  452.             Left_L_L_HeadPtr:=Nil;
  453.             Left_L_L_TailPtr:=Nil;
  454.           End; { Else }
  455.         { split right half of linked list }
  456.         If L_L_SplitKey<>L_L_TailPtr Then
  457.           Begin
  458.             Right_L_L_HeadPtr:=L_L_SplitKey^.Back;
  459.             Right_L_L_HeadPtr^.Front:=Nil;
  460.             Right_L_L_TailPtr:=L_L_TailPtr;
  461.           End { If L_L_SplitKey }
  462.         Else
  463.           Begin
  464.             Right_L_L_HeadPtr:=Nil;
  465.             Right_L_L_TailPtr:=Nil;
  466.           End { Else }
  467.       End { If Not EmptyLinkedList }
  468.     Else
  469.       Begin
  470.         Left_L_L_HeadPtr:=Nil;
  471.         Left_L_L_TailPtr:=Nil;
  472.         Right_L_L_HeadPtr:=Nil;
  473.         Right_L_L_TailPtr:=Nil;
  474.         L_L_SplitKey^.Key:='';
  475.         L_L_SplitKey^.Frequency:=0;
  476.       End; { Else }
  477.     L_L_SplitKey^.Front:=Nil;          { disconnect link }
  478.     L_L_SplitKey^.Back:=Nil;           { disconnect link }
  479.   End;    { DetermineSplitKey }
  480.  
  481.  
  482.  
  483.   Procedure RemoveElementFromLinkedList(Var L_L_HeadPtr,
  484.                                             L_L_TailPtr,
  485.                                             RemoveElement:L_L_ElementPtr);
  486.  
  487.   { This procedure removes the passed element from the passed doubly linked
  488.     list.
  489.  
  490.     Example Linked List:
  491.  
  492.                                ______
  493.                 __       __   |  __  |   __       __       __
  494.         Nil<---|__|<--->|__|<-- |__| -->|__|<--->|__|<--->|__|--->Nil
  495.  
  496.         HeadPtr--^                ^--RemoveElement          ^--TailPtr
  497.  
  498.  
  499.     Note that there are four special cases when deleting an element from the
  500.     passed linked list:
  501.  
  502.         1. Delete last remaining element from linked list.
  503.         2. Delete element from front of linked list.
  504.         3. Delete element from end of linked list.
  505.         4. Delete element from middle of linked list.           }
  506.  
  507.   Begin   { RemoveElementFromLinkedList }
  508.     { Special Case One - Check if deleting last remaining element from linked list }
  509.     If L_L_HeadPtr=L_L_TailPtr Then
  510.       Begin { delete last remaining element from linked list }
  511.         L_L_HeadPtr:=Nil;
  512.         L_L_TailPtr:=Nil;
  513.       End { If L_L_HeadPtr }
  514.     Else
  515.       { Special Case Two - Check if deleting element from front of linked list }
  516.       If L_L_HeadPtr=RemoveElement Then
  517.         Begin { delete element from front of linked list }
  518.           RemoveElement^.Back^.Front:=Nil; { release link with list }
  519.           L_L_HeadPtr:=RemoveElement^.Back; { increment head pointer }
  520.         End { If L_L_HeadPtr }
  521.       Else
  522.         { Special Case Three - Check if deleting element from rear of linked list }
  523.         If L_L_TailPtr=RemoveElement Then
  524.           Begin { delete element from end of linked list }
  525.             RemoveElement^.Front^.Back:=Nil; { release link with list }
  526.             L_L_TailPtr:=RemoveElement^.Front; { decrement tail pointer }
  527.           End { If L_L_TailPtr }
  528.         Else
  529.           { Special Case Four - Deleting element from middle of linked list }
  530.           Begin
  531.             RemoveElement^.Front^.Back:=RemoveElement^.Back; { re-link list }
  532.             RemoveElement^.Back^.Front:=RemoveElement^.Front; { re-link list }
  533.           End; { Else }
  534.   End;    { RemoveElementFromLinkedList }
  535.  
  536.  
  537.  
  538.   Procedure DetermineMostFrequentKey(Var L_L_HeadPtr,
  539.                                          L_L_TailPtr,
  540.                                          FrequentKey:L_L_ElementPtr);
  541.  
  542.   { This procedure determines the most frequent occurring look-up key in the
  543.     passed linked list of look-up keys, using their corresponding frequency
  544.     values.
  545.  
  546.     This procedure then returns this most frequently occurring key to the
  547.     calling routine, while also removing the key from the passed linked list.
  548.  
  549.     Observe that this procedure uses a sequential search to find the most
  550.     frequently occurring key.  This algorithm is slow and clearly could be
  551.     improved by keeping the keys sorted both in lexical and frequency order.
  552.     Since the median split tree is normally only built once and then used, it
  553.     was decided to keep this implementation simple. }
  554.  
  555.   Var
  556.     L_L_Element:L_L_ElementPtr;         { a pointer used in traversing the passed linked list }
  557.  
  558.   Begin   { DetermineMostFrequentKey }
  559.     If Not EmptyLinkedList(L_L_HeadPtr) Then
  560.       Begin { determine most frequent key in linked list }
  561.         FrequentKey:=L_L_HeadPtr;      { initialize the most frequent key pointer }
  562.         L_L_Element:=L_L_HeadPtr;      { initialize linked list traversing pointer }
  563.         While L_L_Element<>L_L_TailPtr Do
  564.           Begin
  565.             L_L_Element:=L_L_Element^.Back; { increment the traversing pointer }
  566.             If L_L_Element^.Frequency>FrequentKey^.Frequency Then
  567.               FrequentKey:=L_L_Element;
  568.           End; { While L_L_Element }
  569.         RemoveElementFromLinkedList(L_L_HeadPtr,
  570.                                     L_L_TailPtr,
  571.                                     FrequentKey);
  572.         FrequentKey^.Front:=Nil;
  573.         FrequentKey^.Back:=Nil;
  574.       End { If Not EmptyLinkedList }
  575.     Else
  576.       With FrequentKey^ Do
  577.         Begin { linked list is empty, hence return an empty entry }
  578.           Key:='';
  579.           Frequency:=0;
  580.           Front:=Nil;
  581.           Back:=Nil;
  582.         End; { With FrequentKey }
  583.   End;    { DetermineMostFrequentKey }
  584.  
  585.  
  586.  
  587.   Procedure Fill_MST_Node(Var L_L_HeadPtr,
  588.                               L_L_TailPtr:      L_L_ElementPtr;
  589.                           Var TreeNode:         TreeNodePtr;
  590.                           Var Left_L_L_HeadPtr,
  591.                               Left_L_L_TailPtr,
  592.                               Right_L_L_HeadPtr,
  593.                               Right_L_L_TailPtr:L_L_ElementPtr);
  594.  
  595.   { This procedure takes the passed linked list of look-up keys, takes the n
  596.     most frequently occurring keys from the linked list, places them into the
  597.     passed key array, sorts the keys according to their lexical order,
  598.     determines the split key and then splits the linked list accordingly, and
  599.     finally returns the passed tree node and the split linked list back to the
  600.     calling routine. }
  601.  
  602.   Var
  603.     ArrayIndex:Integer;                { an index counter used in filling the look-up key array }
  604.     L_L_Element:L_L_ElementPtr;        { a pointer to a removed linked list element }
  605.  
  606.   Begin   { Fill_MST_Node }
  607.     { fill the node's key array }
  608.     For ArrayIndex:=1 To MAX_KEYS_PER_NODE Do
  609.       Begin
  610.         DetermineMostFrequentKey(L_L_HeadPtr,
  611.                                  L_L_TailPtr,
  612.                                  L_L_Element);
  613.         With TreeNode^ Do
  614.           Begin
  615.             NodeKeys[ArrayIndex].Key:=L_L_Element^.Key;
  616.             NodeKeys[ArrayIndex].Frequency:=L_L_Element^.Frequency;
  617.           End; { With TreeNode }
  618.       End; { For ArrayElement }
  619.     SortKeyArray(TreeNode^.NodeKeys);  { sort the keys according to their lexical order }
  620.     { determine split key and split linked list }
  621.     DetermineSplitKey(L_L_HeadPtr,
  622.                       L_L_TailPtr,
  623.                       L_L_Element,
  624.                       Left_L_L_HeadPtr,
  625.                       Left_L_L_TailPtr,
  626.                       Right_L_L_HeadPtr,
  627.                       Right_L_L_TailPtr);
  628.     With TreeNode^ Do
  629.       Begin
  630.         SplitKey.Key:=L_L_Element^.Key;
  631.         SplitKey.Frequency:=L_L_Element^.Frequency;
  632.         Left:=Nil;                     { set left child ptr to Nil }
  633.         Right:=Nil;                    { set right child ptr to Nil }
  634.       End; { With TreeNode^ }
  635.   End;    { Fill_MST_Node }
  636.  
  637.  
  638.  
  639.   Procedure Build_MST_Node(Var L_L_HeadPtr,
  640.                                L_L_TailPtr:   L_L_ElementPtr;
  641.                            Var ParentTreeNode:TreeNodePtr;
  642.                                ParentBranch:  ChildType);
  643.  
  644.   { This recursive procedure does the actual building of the n degree median
  645.     split tree. }
  646.  
  647.   Var
  648.     CurrentTreeNode:TreeNodePtr;       { a MST node pointer used in adding new nodes to the MST }
  649.     Left_L_L_HeadPtr:L_L_ElementPtr;   { pointer to the newly split linked list of look-up keys }
  650.     Left_L_L_TailPtr:L_L_ElementPtr;   { pointer to the newly split linked list of look-up keys }
  651.     Right_L_L_HeadPtr:L_L_ElementPtr;  { pointer to the newly split linked list of look-up keys }
  652.     Right_L_L_TailPtr:L_L_ElementPtr;  { pointer to the newly split linked list of look-up keys }
  653.  
  654.   Begin   { Build_MST_Node }
  655.     If Not EmptyLinkedList(L_L_HeadPtr) Then
  656.       Begin
  657.         { build current node }
  658.         New(CurrentTreeNode);          { allocate an MST node }
  659.         Fill_MST_Node(L_L_HeadPtr,     { fill the MST node with keys from the linked list }
  660.                       L_L_TailPtr,
  661.                       CurrentTreeNode,
  662.                       Left_L_L_HeadPtr,
  663.                       Left_L_L_TailPtr,
  664.                       Right_L_L_HeadPtr,
  665.                       Right_L_L_TailPtr);
  666.         { connect current tree node to parent tree node according to passed parent branch ( or child type ) }
  667.         Case ParentBranch Of
  668.           ROOT  : MST_RootPtr:=CurrentTreeNode;
  669.           LEFT  : ParentTreeNode^.Left:=CurrentTreeNode;
  670.           RIGHT : ParentTreeNode^.Right:=CurrentTreeNode;
  671.         End; { Case ParentBranch }
  672.         { build left son }
  673.         Build_MST_Node(Left_L_L_HeadPtr,
  674.                        Left_L_L_TailPtr,
  675.                        CurrentTreeNode,
  676.                        LEFT);
  677.         { build right son }
  678.         Build_MST_Node(Right_L_L_HeadPtr,
  679.                        Right_L_L_TailPtr,
  680.                        CurrentTreeNode,
  681.                        RIGHT);
  682.       End; { If Not EmptyLinkedList }
  683.   End;    { Build_MST_Node }
  684.  
  685.  
  686.  
  687.  
  688. Begin   { Build_MST_Module }
  689.   Build_MST_Node(L_L_HeadPtr,
  690.                  L_L_TailPtr,
  691.                  MST_RootPtr,
  692.                  ROOT);
  693. End;    { Build_MST_Module }
  694.  
  695.  
  696.  
  697. Procedure Print_MST_Module;
  698.  
  699. { *************************************************************************** }
  700. { *                                                                         * }
  701. { *                     PRINT MEDIAN SPLIT TREE MODULE                      * }
  702. { *                                                                         * }
  703. { *    This module prints out the median split tree node data in a form     * }
  704. { *    representing the tree structure that it is stored in.  It calls a    * }
  705. { *    recursive inorder traversal procedure 'PrintNodesInorder' that       * }
  706. { *    travels down the right most subtree first.                           * }
  707. { *                                                                         * }
  708. { *************************************************************************** }
  709.  
  710. Const
  711.   Offset:Integer=15;                   { column offset used in printing of each tree level }
  712.  
  713. Type
  714.   ChildType=(LEFT,RIGHT,ROOT);         { an enumerated data type used in printing out the proper branch for each child node }
  715.  
  716.  
  717.  
  718.   Procedure Print_MST_Node(   Node:  TreeNodePtr;
  719.                               Level: Integer;
  720.                               Branch:ChildType);
  721.  
  722.   { This procedure prints out the MST node with the proper branch attached. }
  723.  
  724.   Var
  725.     NodeElement:Integer;               { a index counter to an array element }
  726.  
  727.   Begin   { Print_MST_Node }
  728.     If Node=Nil Then
  729.       Case Branch Of
  730.         ROOT  : Begin
  731.                   WriteLn(' ':Level*Offset,'Nil');    { write out split value }
  732.                   WriteLn(' ':Level*Offset,'^ Split Key');
  733.                   For NodeElement:=MAX_KEYS_PER_NODE DownTo ((MAX_KEYS_PER_NODE Div 2)+2) Do
  734.                     WriteLn(' ':Level*Offset,'Nil'); { write out right elements }
  735.                   WriteLn('-':Level*Offset,'Nil');   { write out center element }
  736.                   For NodeElement:=(MAX_KEYS_PER_NODE Div 2) DownTo 1 Do
  737.                     WriteLn(' ':Level*Offset,'Nil');  { write out left elements }
  738.                 End;
  739.         RIGHT : Begin
  740.                   WriteLn(' ':Level*Offset,'Nil');    { write out split value }
  741.                   WriteLn(' ':Level*Offset,'^ Split Key');
  742.                   For NodeElement:=MAX_KEYS_PER_NODE DownTo 2 Do
  743.                     WriteLn(' ':Level*Offset,'Nil'); { write out elements }
  744.                   WriteLn('/':Level*Offset,'Nil');   { write out left most element }
  745.                 End;
  746.         LEFT  : Begin
  747.                   WriteLn(' ':Level*Offset,'Nil');    { write out split value }
  748.                   WriteLn(' ':Level*Offset,'^ Split Key');
  749.                   WriteLn('\':Level*Offset,'Nil');   { write out right most element }
  750.                   For NodeElement:=MAX_KEYS_PER_NODE-1 Downto 1 Do
  751.                     WriteLn(' ':Level*Offset,'Nil'); { write out elements }
  752.                 End;
  753.       End { Case Branch }
  754.     Else { not a Nil node }
  755.       With Node^ Do
  756.         Case Branch Of
  757.           ROOT  : Begin
  758.                     If Length(SplitKey.Key)<>0 Then
  759.                       WriteLn(' ':Level*Offset,SplitKey.Key,
  760.                               ' ',SplitKey.Frequency)
  761.                     Else
  762.                       WriteLn(' ':Level*Offset,'Nil');
  763.                     WriteLn(' ':Level*Offset,'^ Split Key');
  764.                     For NodeElement:=MAX_KEYS_PER_NODE DownTo ((MAX_KEYS_PER_NODE Div 2)+2) Do
  765.                       If Length(NodeKeys[NodeElement].Key)<>0 Then
  766.                         WriteLn(' ':Level*Offset,NodeKeys[NodeElement].Key,
  767.                                 ' ',NodeKeys[NodeElement].Frequency)
  768.                       Else
  769.                         WriteLn(' ':Level*Offset,'Nil');
  770.                     If Length(NodeKeys[(MAX_KEYS_PER_NODE Div 2)+1].Key)<>0 Then
  771.                       WriteLn('-':Level*Offset,NodeKeys[(MAX_KEYS_PER_NODE Div 2)+1].Key,
  772.                               ' ',NodeKeys[(MAX_KEYS_PER_NODE Div 2)+1].Frequency)
  773.                     Else
  774.                       WriteLn('-':Level*Offset,'Nil');
  775.                     For NodeElement:=(MAX_KEYS_PER_NODE Div 2) DownTo 1 Do
  776.                       If Length(NodeKeys[NodeElement].Key)<>0 Then
  777.                         WriteLn(' ':Level*Offset,NodeKeys[NodeElement].Key,
  778.                                 ' ',NodeKeys[NodeElement].Frequency)
  779.                       Else
  780.                         WriteLn(' ':Level*Offset,'Nil');
  781.                   End;
  782.           RIGHT : Begin
  783.                     If Length(SplitKey.Key)<>0 Then
  784.                       WriteLn(' ':Level*Offset,SplitKey.Key,
  785.                               ' ',SplitKey.Frequency)
  786.                     Else
  787.                       WriteLn(' ':Level*Offset,'Nil');
  788.                     WriteLn(' ':Level*Offset,'^ Split Key');
  789.                     For NodeElement:=MAX_KEYS_PER_NODE DownTo 2 Do
  790.                       If Length(NodeKeys[NodeElement].Key)<>0 Then
  791.                         WriteLn(' ':Level*Offset,NodeKeys[NodeElement].Key,
  792.                                 ' ',NodeKeys[NodeElement].Frequency)
  793.                       Else
  794.                         WriteLn(' ':Level*Offset,'Nil');
  795.                     If Length(NodeKeys[1].Key)<>0 Then
  796.                       WriteLn('/':Level*Offset,NodeKeys[1].Key,
  797.                               ' ',NodeKeys[1].Frequency)
  798.                     Else
  799.                       WriteLn('/':Level*Offset,'Nil');
  800.                   End;
  801.           LEFT  : Begin
  802.                     If Length(SplitKey.Key)<>0 Then
  803.                       WriteLn(' ':Level*Offset,SplitKey.Key,
  804.                               ' ',SplitKey.Frequency)
  805.                     Else
  806.                       WriteLn(' ':Level*Offset,'Nil');
  807.                     WriteLn(' ':Level*Offset,'^ Split Key');
  808.                     If Length(NodeKeys[MAX_KEYS_PER_NODE].Key)<>0 Then
  809.                       WriteLn('\':Level*Offset,NodeKeys[MAX_KEYS_PER_NODE].Key,
  810.                               ' ',NodeKeys[MAX_KEYS_PER_NODE].Frequency)
  811.                     Else
  812.                       WriteLn('\':Level*Offset,'Nil');
  813.                     For NodeElement:=MAX_KEYS_PER_NODE-1 DownTo 1 Do
  814.                       If Length(NodeKeys[NodeElement].Key)<>0 Then
  815.                         WriteLn(' ':Level*Offset,NodeKeys[NodeElement].Key,
  816.                                 ' ',NodeKeys[NodeElement].Frequency)
  817.                       Else
  818.                         WriteLn(' ':Level*Offset,'Nil');
  819.                   End;
  820.         End; { Case Branch }
  821.   End;    { Print_MST_Node }
  822.  
  823.  
  824.  
  825.   Procedure PrintNodesInorder(    Node:  TreeNodePtr;
  826.                                   Level: Integer;
  827.                                   Branch:ChildType);
  828.  
  829.   { This recursive procedure is used to print the nodes of the median split
  830.     tree.  It traverses the tree using an inorder traversal that goes to the
  831.     right most subtree first. }
  832.  
  833.   Begin    { PrintNodesInorder }
  834.     If Node=Nil Then                   { Nil child node }
  835.       Print_MST_Node(Node,level,Branch)
  836.     Else
  837.       Begin
  838.         PrintNodesInorder(Node^.Right,Level+1,RIGHT); { traverse right subtree }
  839.         Print_MST_Node(Node,Level,Branch);
  840.         PrintNodesInorder(Node^.Left,Level+1,LEFT); { traverse left subtree }
  841.       End; { Else }
  842.   End;     { PrintNodesInorder }
  843.  
  844.  
  845.  
  846. Begin   { Print_MST_Module }
  847.   WriteLn;
  848.   WriteLn;
  849.   WriteLn('MEDIAN SPLIT TREE FOLLOWS:');
  850.   WriteLn('--------------------------');
  851.   WriteLn;
  852.   WriteLn;
  853.   PrintNodesInorder(MST_RootPtr,0,ROOT);
  854. End;    { Print_MST_Module }
  855.  
  856.  
  857.  
  858. Procedure PrintQueryHeading;
  859.  
  860. { This procedure prints out a heading for the search for matching look-up keys
  861.   for the file of queries. }
  862.  
  863. Begin   { PrintQueryHeading }
  864.   WriteLn;
  865.   WriteLn;
  866.   WriteLn('QUERY SEARCH FOLLOWS:');
  867.   WriteLn('---------------------');
  868.   WriteLn;
  869.   WriteLn;
  870. End;    { PrintQueryHeading }
  871.  
  872.  
  873.  
  874. Procedure Search_MST_Module(    Query:              KeyString;
  875.                             Var Found:              Boolean;
  876.                             Var NumberOfComparisons:Integer);
  877.  
  878. { *************************************************************************** }
  879. { *                                                                         * }
  880. { *                     SEARCH MEDIAN SPLIT TREE MODULE                     * }
  881. { *                                                                         * }
  882. { *    This module controls the looking up of the passed quiery with the    * }
  883. { *    entries in the median split tree.                                    * }
  884. { *                                                                         * }
  885. { *************************************************************************** }
  886.  
  887.  
  888.  
  889.   Procedure InitModule;
  890.  
  891.   { This procedure initializes the variables used in this module. }
  892.  
  893.   Begin   { InitModule }
  894.     Found:=False;
  895.     NumberOfComparisons:=0;
  896.   End;    { InitModule }
  897.  
  898.  
  899.  
  900.   Procedure Search_MST_Node(    ArrayOfKeys:        KeyArray;
  901.                                 Query:              KeyString;
  902.                             Var Found:              Boolean;
  903.                             Var NumberOfComparisons:Integer);
  904.  
  905.   { This procedure performs a binary search on the passed array of look-up keys
  906.     to try to find a match with the passed query.  If a match is found then the
  907.     Found boolean is returned as true.  The NumberOfComparisons is also
  908.     returned. }
  909.  
  910.   Var
  911.     LowElement:Integer;                { an index to a low element in the passed array }
  912.     MidElement:Integer;                { an index to a middle element in the passed array }
  913.     HighElement:Integer;               { an index to a high element in the passed array }
  914.  
  915.   Begin   { Search_MST_Node }
  916.     { initialize the index variables }
  917.     LowElement:=1;
  918.     HighElement:=MAX_KEYS_PER_NODE;
  919.     While (LowElement<=HighElement) And ( Not Found) Do
  920.       Begin
  921.         NumberOfComparisons:=NumberOfComparisons+1; { increment counter }
  922.         MidElement:=(LowElement+HighElement) Div 2; { determine mid point }
  923.         If Query<ArrayOfKeys[MidElement].Key Then
  924.           HighElement:=MidElement-1
  925.         Else
  926.           If Query>ArrayOfKeys[MidElement].Key Then
  927.             LowElement:=MidElement+1
  928.           Else
  929.             Found:=True;
  930.       End; { While LowElement }
  931.   End;    { Search_MST_Node }
  932.  
  933.  
  934.  
  935.   Procedure  Search_MST(    CurrentTreeNode:    TreeNodePtr;
  936.                             Query:              KeyString;
  937.                         Var Found:              Boolean;
  938.                         Var NumberOfComparisons:Integer);
  939.  
  940.   Begin   { Search_MST }
  941.     If CurrentTreeNode<>Nil Then
  942.       Begin
  943.         Search_MST_Node(CurrentTreeNode^.NodeKeys,
  944.                         Query,
  945.                         Found,
  946.                         NumberOfComparisons);
  947.         If (Not Found) And (CurrentTreeNode^.SplitKey.Key<>'') Then
  948.           Begin
  949.             NumberOfComparisons:=NumberOfComparisons+1; { increment counter }
  950.             If Query=CurrentTreeNode^.SplitKey.Key Then
  951.               Found:=True
  952.             Else { continue search }
  953.               Begin
  954.                 If Query<CurrentTreeNode^.SplitKey.Key Then
  955.                   CurrentTreeNode:=CurrentTreeNode^.Left { point to left subtree }
  956.                 Else
  957.                   CurrentTreeNode:=CurrentTreeNode^.Right; { point to right subtree }
  958.                 Search_MST(CurrentTreeNode,
  959.                            Query,
  960.                            Found,
  961.                            NumberOfComparisons);
  962.               End; { Else }
  963.           End; { If Not Found }
  964.       End; { If CurrentTreeNode }
  965.   End;    { Search_MST }
  966.  
  967.  
  968.  
  969. Begin   { Search_MST_Module }
  970.   InitModule;
  971.   Search_MST(MST_RootPtr,
  972.              Query,
  973.              Found,
  974.              NumberOfComparisons);
  975. End;    { Search_MST_Module }
  976.  
  977.  
  978.  
  979. Procedure PrintSearchResults(    Query:              KeyString;
  980.                                  Found:              Boolean;
  981.                                  NumberOfComparisons:Integer);
  982.  
  983. { This procedure prints out the result from the look-up key search. }
  984.  
  985. Begin   { PrintSearchResults }
  986.   If Not Found Then
  987.     WriteLn('The query ',Query,' was not found to exist in the median split tree.')
  988.   Else
  989.     WriteLn('The query ',Query,' was found in ',NumberOfComparisons,' comparisons.');
  990. End;    { PrintSearchResults }
  991.  
  992.  
  993.  
  994. Procedure ReadQueryFile;
  995.  
  996. { This procedure reads queries from a query file, passes the query to a look-up
  997.   module to see idf the query exists in the MST and finally passes the result
  998.   of the search to a procedure to print the results of the search. }
  999.  
  1000. Const
  1001.   FILE_NAME='Query1.Dat';              { name of the query data file }
  1002.  
  1003. Type
  1004.   FileEntryString=String[MAX_KEY_SIZE]; { a string type used to store file entries }
  1005.  
  1006. Var
  1007.   QueryFile:Text;                      { text file }
  1008.   FileEntry:FileEntryString;           { variable used in reading the queries in teh query file }
  1009.   Found:Boolean;                       { a boolean flag used to determine if a query is in the MST }
  1010.   NumberOfComparisons:Integer;         { a counter used to determine the number of comparisons required
  1011.                                          to find a particular query in the MST }
  1012.  
  1013. Begin   { ReadQueryFile }
  1014.   Assign(QueryFile,FILE_NAME);         { assign to a disk file }
  1015.   Reset(QueryFile);                    { open the file for reading }
  1016.   Repeat
  1017.     ReadLn(QueryFile,FileEntry);        { read in the file entry }
  1018.     Search_MST_Module(FileEntry,
  1019.                       Found,
  1020.                       NumberOfComparisons);
  1021.     PrintSearchResults(FileEntry,
  1022.                        Found,
  1023.                        NumberOfComparisons);
  1024.   Until Eof(QueryFile);                { read until end of file found }
  1025.   Close(QueryFile);                    { close the disk file }
  1026. End;    { ReadQueryFile }
  1027.  
  1028.  
  1029.  
  1030. Begin   { Main Program }
  1031.   InitProgram;
  1032.   HardCopy(False);
  1033.   ReadInputFileModule;
  1034.   PrintLinkedList(L_L_HeadPtr,L_L_TailPtr);
  1035.   Build_MST_Module;
  1036.   Print_MST_Module;
  1037.   PrintQueryHeading;
  1038.   ReadQueryFile;
  1039. End.    { Main Program }
  1040.